//
// Created by LONG on 2020/10/27.
//

#include <stdio.h>
#include <iostream>
#include "../lib/doctest.h"
#include "../../include/utils/wheelTimer.h"

using namespace std;

static volatile uint32_t gTimerCallBackValue = 0;
static void ut_timer_call_back(void *arg)
{
    gTimerCallBackValue++;
}

static S_WtWheel *gp_wheel_ut;

static void ut_timer_call_back2(void *arg)
{
    int32_t rc;
    uint32_t delay;
    S_WtTimer *timer = (S_WtTimer *)arg;

    rc = WT_StopTimer(&timer[random() % 16]);
    REQUIRE_EQ(CSP_RC_OK, rc);
    delay = random() % 16;
    delay = MAX(1, delay);
    rc = WT_StartTimer(gp_wheel_ut, timer + (random() % 16), ut_timer_call_back2, timer, random() % 2, delay);
    REQUIRE_EQ(CSP_RC_OK, rc);

    rc = WT_StopTimer(&timer[random() % 16]);
    REQUIRE_EQ(CSP_RC_OK, rc);
    delay = random() % 16;
    delay = MAX(1, delay);
    rc = WT_StartTimer(gp_wheel_ut, timer + (random() % 16), ut_timer_call_back2, timer, random() % 2, delay);
    REQUIRE_EQ(CSP_RC_OK, rc);

    rc = WT_StopTimer(&timer[random() % 16]);
    REQUIRE_EQ(CSP_RC_OK, rc);
    delay = random() % 16;
    delay = MAX(1, delay);
    rc = WT_StartTimer(gp_wheel_ut, timer + (random() % 16), ut_timer_call_back2, timer, random() % 2, delay);
    REQUIRE_EQ(CSP_RC_OK, rc);
}

TEST_SUITE ("WHEEL_TIMER_TS")
{
    TEST_CASE ("WHEEL_TIMER_TC")
    {
        SUBCASE("WHEEL_TIMER_SC_BASE")
        {
            int32_t rc;
            uint32_t loop;
            S_WtWheel *wheel;
            S_WtTimer timer;

            wheel = WT_CreateWheel("test", 1024);
            REQUIRE(wheel);

            rc = WT_InitTimer(&timer);
            REQUIRE_EQ(CSP_RC_OK, rc);

            rc = WT_StartTimer(wheel, &timer, ut_timer_call_back, NULL, TIMER_TYPE_ONCE, 0);
            REQUIRE_EQ(CSP_RC_ER, rc);

            rc = WT_StartTimer(wheel, &timer, ut_timer_call_back, NULL, TIMER_TYPE_ONCE, 1025);
            REQUIRE_EQ(CSP_RC_ER, rc);

            gTimerCallBackValue = 0;
            rc = WT_StartTimer(wheel, &timer, ut_timer_call_back, NULL, TIMER_TYPE_ONCE, 10);
            REQUIRE_EQ(CSP_RC_OK, rc);
            WT_Run(wheel, 10);
            REQUIRE_EQ((uint32_t)1, gTimerCallBackValue);
            WT_Run(wheel, 1);
            REQUIRE_EQ((uint32_t)1, gTimerCallBackValue);

            gTimerCallBackValue = 0;
            rc = WT_StartTimer(wheel, &timer, ut_timer_call_back, NULL, TIMER_TYPE_ONCE, 10);
            REQUIRE_EQ(CSP_RC_OK, rc);
            for (loop = 0; loop < 10; loop++)
            {
                WT_Run(wheel, 1);
                if (loop < 9)
                {
                    REQUIRE_EQ((uint32_t)0, gTimerCallBackValue);
                }
                else
                {
                    REQUIRE_EQ((uint32_t)1, gTimerCallBackValue);
                }
            }

            gTimerCallBackValue = 0;
            rc = WT_StartTimer(wheel, &timer, ut_timer_call_back, NULL, TIMER_TYPE_ONCE, 10);
            REQUIRE_EQ(CSP_RC_OK, rc);
            rc = WT_StopTimer(&timer);
            REQUIRE_EQ(CSP_RC_OK, rc);
            WT_Run(wheel, 10);
            REQUIRE_EQ((uint32_t)0, gTimerCallBackValue);

            rc = WT_FinalTimer(&timer);
            REQUIRE_EQ(CSP_RC_OK, rc);

            WT_DestroyWheel(wheel);
        }

        SUBCASE("WHEEL_TIMER_SC_LOOP")
        {
            int32_t rc;
            S_WtWheel *wheel;
            S_WtTimer timer;

            gTimerCallBackValue = 0;

            wheel = WT_CreateWheel("test", 1024);
            REQUIRE(wheel);

            rc = WT_InitTimer(&timer);
            REQUIRE_EQ(CSP_RC_OK, rc);

            rc = WT_StartTimer(wheel, &timer, ut_timer_call_back, NULL, TIMER_TYPE_LOOP, 10);
            REQUIRE_EQ(CSP_RC_OK, rc);
            rc = WT_GetTimerState(&timer);
            REQUIRE_EQ(TIMER_STATE_START, rc);
            WT_Run(wheel, 10);
            REQUIRE_EQ((uint32_t)1, gTimerCallBackValue);
            WT_Run(wheel, 10);
            REQUIRE_EQ((uint32_t)2, gTimerCallBackValue);
            WT_Run(wheel, 10);
            REQUIRE_EQ((uint32_t)3, gTimerCallBackValue);
            rc = WT_GetTimerState(&timer);
            REQUIRE_EQ(TIMER_STATE_START, rc);

            rc = WT_StopTimer(&timer);
            REQUIRE_EQ(CSP_RC_OK, rc);
            rc = WT_GetTimerState(&timer);
            REQUIRE_EQ(TIMER_STATE_STOP, rc);

            rc = WT_StartTimer(wheel, &timer, ut_timer_call_back, NULL, TIMER_TYPE_ONCE, 10);
            REQUIRE_EQ(CSP_RC_OK, rc);
            WT_Run(wheel, 10);
            REQUIRE_EQ((uint32_t)4, gTimerCallBackValue);
            rc = WT_GetTimerState(&timer);
            REQUIRE_EQ(TIMER_STATE_TIMEOUT, rc);

            rc = WT_StopTimer(&timer);
            REQUIRE_EQ(CSP_RC_OK, rc);
            rc = WT_GetTimerState(&timer);
            REQUIRE_EQ(TIMER_STATE_STOP, rc);

            rc = WT_FinalTimer(&timer);
            REQUIRE_EQ(CSP_RC_OK, rc);

            WT_DestroyWheel(wheel);
        }

        SUBCASE("WHEEL_TIMER_SC_START_STOP")
        {
            int32_t rc;
            uint32_t loop;
            uint32_t delay;
            S_WtTimer timer[16];

            gp_wheel_ut = WT_CreateWheel("test", 16);
            REQUIRE(gp_wheel_ut);

            for (loop = 0; loop < 16; loop++)
            {
                rc = WT_InitTimer(&timer[loop]);
                REQUIRE_EQ(CSP_RC_OK, rc);

                delay = random() % 16;
                delay = MAX(1, delay);
                rc = WT_StartTimer(gp_wheel_ut, &timer[loop], ut_timer_call_back2, timer, random() % 2, delay);
                REQUIRE_EQ(CSP_RC_OK, rc);
            }

            for (loop = 0; loop < 100000; loop++)
            {
                WT_Run(gp_wheel_ut, random() % 16);
            }

            for (loop = 0; loop < 16; loop++)
            {
                rc = WT_FinalTimer(&timer[loop]);
                REQUIRE_EQ(CSP_RC_OK, rc);
            }

            WT_DestroyWheel(gp_wheel_ut);
        }
    }
}

